package com.gzc.just.play.last.war.robotclient.statemachine;

import com.github.oxo42.stateless4j.StateMachine;
import com.github.oxo42.stateless4j.StateMachineConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Wrapper around Stateless4j to coordinate robot connection state transitions.
 */
public class RobotStateMachine {

    private static final Logger log = LoggerFactory.getLogger(RobotStateMachine.class);

    private final StateMachine<RobotState, RobotEvent> stateMachine;

    public RobotStateMachine() {
        StateMachineConfig<RobotState, RobotEvent> config = new StateMachineConfig<>();
        configureLoginState(config);
        configureBalanceState(config);
        configureGateState(config);
        this.stateMachine = new StateMachine<>(RobotState.LOGIN_SERVER, config);
    }

    private void configureLoginState(StateMachineConfig<RobotState, RobotEvent> config) {
        config.configure(RobotState.LOGIN_SERVER)
                .permit(RobotEvent.SWITCH_TO_BALANCE, RobotState.BALANCE_SERVER)
                .permit(RobotEvent.RESET, RobotState.LOGIN_SERVER);
    }

    private void configureBalanceState(StateMachineConfig<RobotState, RobotEvent> config) {
        config.configure(RobotState.BALANCE_SERVER)
                .permit(RobotEvent.SWITCH_TO_GATE, RobotState.GATE_SERVER)
                .permit(RobotEvent.SWITCH_TO_LOGIN, RobotState.LOGIN_SERVER)
                .permit(RobotEvent.RESET, RobotState.LOGIN_SERVER);
    }

    private void configureGateState(StateMachineConfig<RobotState, RobotEvent> config) {
        config.configure(RobotState.GATE_SERVER)
                .permit(RobotEvent.SWITCH_TO_LOGIN, RobotState.LOGIN_SERVER)
                .permit(RobotEvent.RESET, RobotState.LOGIN_SERVER);
    }

    public RobotState getCurrentState() {
        return stateMachine.getState();
    }

    public void switchTo(RobotState target) {
        RobotState current = stateMachine.getState();
        if (current == target) {
            return;
        }
        RobotEvent trigger = mapToTrigger(target);
        if (trigger == null) {
            log.warn("No trigger defined to switch to state {}", target);
            return;
        }
        fireIfAllowed(trigger);
    }

    public void reset() {
        fireIfAllowed(RobotEvent.RESET);
    }

    private RobotEvent mapToTrigger(RobotState target) {
        return switch (target) {
            case LOGIN_SERVER -> RobotEvent.SWITCH_TO_LOGIN;
            case BALANCE_SERVER -> RobotEvent.SWITCH_TO_BALANCE;
            case GATE_SERVER -> RobotEvent.SWITCH_TO_GATE;
        };
    }

    private void fireIfAllowed(RobotEvent event) {
        if (stateMachine.canFire(event)) {
            stateMachine.fire(event);
        } else {
            log.debug("State machine ignored event {} while in state {}", event, stateMachine.getState());
        }
    }
}
